/*
 * Notify.cpp
 *
 *  Created on: 2011-5-29
 *      Author: yangentao@gmail.com
 */

#include "../pre.h"
#include "Notify.h"


Notifable::~Notifable() {
	Notify::Remove(this);
}

namespace Notify {
	typedef signal< void (Notifable* instance, int, any) > NotifySignal;
	typedef map< tuple<Notifable*, int>, boost::shared_ptr<NotifySignal > > NotifySignalStore;

	//flag,  0=get, 1=autoCreate, 2=delete
	NotifySignalStore* GetAllSigs(int flag = 0) {
		static NotifySignalStore* allSig = 0;
		switch(flag) {
		case 1: {
			if(!allSig) {
				allSig = new NotifySignalStore();
			}
		}
		break;
		case 2: {
			if(allSig) {
				allSig->clear();
				delete allSig;
				allSig = 0;
			}
		}
		break;
		}
		return allSig;
	}

	void Fire(Notifable* instance, int msg, any data) {
		NotifySignalStore* all = GetAllSigs(0);
		if(all) {
			NotifySignalStore::iterator it = all->find(make_tuple(instance, msg));
			if(it != all->end()) {
				boost::shared_ptr<NotifySignal> sig = it->second;
				(*sig)(instance, msg, data);
			}
		}
	}
	connection Add(Notifable* instance, int msg, NotifyCallback callback, int group, connect_position pos) {
		NotifySignalStore* all = GetAllSigs(1);
		assert(all);
		boost::shared_ptr<NotifySignal> sig;
		NotifySignalStore::iterator it = all->find(make_tuple(instance, msg));
		if(it == all->end()) {
			sig.reset(new NotifySignal());
			(*all)[make_tuple(instance, msg)] = sig;
		} else {
			sig = it->second;
		}
		if(group) {
			return sig->connect(group, callback, pos);
		}
		return sig->connect(callback, pos);
	}
	void Remove(connection con) {
		con.disconnect();
	}
	void Remove(Notifable* instance, int msg) {
		NotifySignalStore* all = GetAllSigs(0);
		if(all) {
			all->erase(make_tuple(instance, msg));
		}
	}
	void Remove(Notifable* instance) {
		NotifySignalStore* all = GetAllSigs(0);
		if(all) {
			vector<tuple<Notifable*, int> > vec;
			for(NotifySignalStore::iterator it = all->begin(); it != all->end(); ++it) {
				const tuple<Notifable*, int>& t = it->first;
				if(t.get<0>() == instance) {
					vec.push_back(it->first);
				}
			}
			for(size_t i = 0; i < vec.size(); ++i) {
				all->erase(vec[i]);
			}
			if(all->empty()) {
				GetAllSigs(2);
			}
		}
	}
};
